/*******************************************************************************
 *
 * Copyright (C), 1988-1999, Sunell Tech. Co., Ltd.
 *
 * File name: imake.c
 *
 * Description:
 *		Detect changes in a video stream.
 *
 *  Created on:
 *      Author: xiaoyongsheng
 *     Version: V1.0.0
 *
 * History:
 *
 * 	  Date:
 *  Author:
 *     Mod:
 ******************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <fcntl.h>

#include <errno.h>

typedef unsigned char boolean;
#define TRUE		1
#define FALSE		0

#include "imakemdep.h"

#ifndef PATH_MAX
#define PATH_MAX 	1024
#endif

#ifndef CROSSCOMPILE
# 	ifdef USE_CC_E
#  		ifndef DEFAULT_CC
#   		define DEFAULT_CC "cc"
#  		endif
# 	else
#  		ifndef DEFAULT_CPP
#   		ifdef CPP_PROGRAM
#    			define DEFAULT_CPP CPP_PROGRAM
#   		else
#    			define DEFAULT_CPP "/lib/cpp"
#   		endif
#  		endif
# 	endif
#endif

char * cpp = NULL;

char * tmpMakefile  = "/tmp/Imf.XXXXXX";
char * tmpImakefile = "/tmp/IIf.XXXXXX";

char * make_argv[ ARGUMENTS ] = {
#ifdef WIN32
    "nmake"
#else
    "make"
#endif
};

int	make_argindex;
int	cpp_argindex;
char	*Imakefile = NULL;
char	*Makefile  = "Makefile";
char	*Template  = "Imake.tmpl";
char	*ImakefileC= "Imakefile.c";
char	*cleanedImakefile = NULL;
char	*program;

#ifdef CROSSCOMPILEDIR
char *CrossCompileDir = CROSSCOMPILEDIR;
#else
char *CrossCompileDir = "";
#endif

boolean CrossCompiling = FALSE;

boolean verbose = FALSE;
boolean show = TRUE;


void
wrapup(void)
{

}


void
LogMsg(char *x0, char *x1)
{
	int error_number = errno;

	if (error_number) {
		fprintf(stderr, "%s: ", program);
		fprintf(stderr, "%s\n", strerror(error_number));
	}
	fprintf(stderr, "%s: ", program);
	fprintf(stderr, x0, x1);
	fprintf(stderr, "\n");
}


void
LogFatal(char *x0, char *x1)
{
	static boolean entered = FALSE;
	if (entered) {
		return;
	}
	entered = TRUE;

	LogMsg(x0, x1);

	fprintf(stderr, "  Stop.\n");
	wrapup();
	exit(1);
}


char *
Emalloc(int size)
{
	char *p = NULL;
	if ((p = malloc(size)) == NULL) {
		LogFatal("Cannot allocate %d bytes", (char *)(long)size);
	}
	return(p);
}


char *
FindImakefile(char *Imakefile)
{
	if (Imakefile) {
		if (access(Imakefile, R_OK) < 0) {
			LogFatal("Cannot find %s.", Imakefile);
		}
	} else {
		if (access("Imakefile", R_OK) < 0) {
			if (access("imakefile", R_OK) < 0) {
				LogFatal("No description file.", "");
			} else {
				Imakefile = "imakefile";
			}
		} else {
			Imakefile = "Imakefile";
		}
	}
	return(Imakefile);
}

#define ImakefileCHeader 	"/* imake - temporary file */"

void
CheckImakefileC(char *masterc)
{
	char mkcbuf[1024];
	FILE *inFile;

	if (access(masterc, F_OK) == 0) {
		inFile = fopen(masterc, "r");
		if (inFile == NULL) {
			LogFatal("Refuse to overwrite: %s", masterc);
		}
		if ((fgets(mkcbuf, sizeof(mkcbuf), inFile) &&
		     strncmp(mkcbuf, ImakefileCHeader,
		     		 sizeof(ImakefileCHeader) - 1)))
		{
			fclose(inFile);
			LogFatal("Refuse to overwrite: %s", masterc);
		}
		fclose(inFile);
	}
}


char *
CleanCppInput(char *imakefile)
{
	FILE	*outFile = NULL;
	FILE	*inFile;
	char	*buf,		/* buffer for file content */
			*pbuf,		/* walking pointer to buf */
			*punwritten,/* pointer to unwritten portion of buf */
			*ptoken,	/* pointer to # token */
			*pend,		/* pointer to end of # token */
			 savec;		/* temporary character holder */
	int		count;
	struct stat	st;

	if (!(inFile = fopen(imakefile, "r"))) {
		LogFatal("Cannot open %s for input.", imakefile);
	}

	if (fstat(fileno(inFile), &st) < 0) {
		LogFatal("Cannot stat %s for size.", imakefile);
	}

	buf = Emalloc((int)st.st_size + 3);

	count = fread(buf + 2, 1, st.st_size, inFile);
	if (count == 0 && st.st_size != 0) {
		LogFatal("Cannot read %s:", imakefile);
	}

	fclose(inFile);

	buf[0] = '\n';
	buf[1] = '\n';
	buf[count + 2] = '\0';

	punwritten = pbuf = buf + 2;
	while (*pbuf) {
		/* for compatibility, replace make comments for cpp */
	    if (*pbuf == '#' && pbuf[-1] == '\n' && pbuf[-2] != '\\') {
	    	ptoken = pbuf + 1;

	    	while (*ptoken == ' ' || *ptoken == '\t') {
				ptoken++;
	    	}

	    	pend = ptoken;

	    	while (*pend && *pend != ' ' && *pend != '\t' &&
	    		   *pend != '\n' && *pend != '\r') {
				pend++;
			}

			savec = *pend;
			*pend = '\0';
			if (strcmp(ptoken, "define") &&
			    strcmp(ptoken, "if") &&
			    strcmp(ptoken, "ifdef") &&
			    strcmp(ptoken, "ifndef") &&
			    strcmp(ptoken, "include") &&
			    strcmp(ptoken, "line") &&
			    strcmp(ptoken, "else") &&
			    strcmp(ptoken, "elif") &&
			    strcmp(ptoken, "endif") &&
			    strcmp(ptoken, "error") &&
			    strcmp(ptoken, "pragma") &&
			    strcmp(ptoken, "undef")) {

				if (outFile == NULL) {

				}

			}
	    }

	    pbuf++;
	}

	return(imakefile);
}


/*
 * Initialize some variables.
 */
void
initSomeVariables(void)
{
	make_argindex = 0;

	while (make_argv[make_argindex] != NULL) {
		make_argindex++;
	}

	cpp_argindex = 0;

	while (cpp_argv[cpp_argindex] != NULL) {
		cpp_argindex++;
	}

#if defined CROSSCOMPILE
	if (sys == netBSD)
	  if (CrossCompiling) {
	    LogFatal("fix imake to do crosscompiling for NetBSD\n", "");
	  } else
#endif
}


void
AddMakeArg(char *arg)
{
	errno = 0;

	if (make_argindex >= ARGUMENTS - 1) {
		LogFatal("Out of internal storage.", "");
	}

	make_argv[ make_argindex++ ] = arg;
	make_argv[ make_argindex ] = NULL;
}


void
AddCppArg(char *arg)
{
	errno = 0;

	if (cpp_argindex >= ARGUMENTS - 1) {
		LogFatal("Out of internal storage.", "");
	}

	cpp_argv[ cpp_argindex++ ] = arg;
	cpp_argv[ cpp_argindex ] = NULL;
}


void
setOpts(int argc, char **argv)
{
	errno = 0;

	for (argc--, argv++; argc; argc--, argv++) {
		if (argv[0][0] == '-') {
			if (argv[0][1] == 'D') {
				AddCppArg(argv[0]);
			} else if (argv[0][1] == 'I') {
				AddCppArg(argv[0]);
			} else if (argv[0][1] == 'U') {
		    	AddCppArg(argv[0]);
			} else if (argv[0][1] == 'W') {
			    AddCppArg(argv[0]);
			} else if (argv[0][1] == 'f') {
			    if (argv[0][2]) {
					Imakefile = argv[0] + 2;
			    } else {
					argc--, argv++;
					if (!argc) {
				    	LogFatal("No description arg after -f flag", "");
					}
					Imakefile = argv[0];
			    }
			} else if (argv[0][1] == 's') {
		    	if (argv[0][2]) {
					Makefile = ((argv[0][2] == '-') && !argv[0][3]) ?
			    		NULL : argv[0] + 2;
		    	} else {
					argc--, argv++;
					if (!argc) {
			    		LogFatal("No description arg after -s flag", "");
					}
					Makefile = ((argv[0][0] == '-') && !argv[0][1]) ?
			    		NULL : argv[0];
		    	}
		    	show = TRUE;
			} else if (argv[0][1] == 'e') {
		   		Makefile = (argv[0][2] ? argv[0] + 2 : NULL);
		   		show = FALSE;
			} else if (argv[0][1] == 'T') {
		    	if (argv[0][2]) {
					Template = argv[0]+2;
		    	} else {
					argc--, argv++;
					if (!argc) {
			    		LogFatal("No description arg after -T flag", "");
					}
					Template = argv[0];
		    	}
			} else if (argv[0][1] == 'C') {
		    	if (argv[0][2]) {
					ImakefileC = argv[0]+2;
		    	} else {
					argc--, argv++;
					if (!argc) {
			    		LogFatal("No imakeCfile arg after -C flag", "");
					}
					ImakefileC = argv[0];
		    	}
			} else if (argv[0][1] == 'v') {
		    	verbose = TRUE;
			} else {
		    	AddMakeArg(argv[0]);
			}
		} else {
			AddMakeArg(argv[0]);
		}
	}

#ifndef CROSSCOMPILE
#	ifdef USE_CC_E

	if (!cpp) {
		AddCppArg("-E");

#		ifdef __GNUC__
		if (verbose) {
		    AddCppArg("-v");
		}
#		endif

		cpp = DEFAULT_CC;
	}

# 	else //!USE_CC_E

	if (!cpp) {
		cpp = DEFAULT_CPP;
	}
# 	endif

#else // CROSSCOMPILE
	if (!cpp) {
		cpp = CrossCompileCPP();
	}
#endif // CROSSCOMPILE

	cpp_argv[0] = cpp;
	AddCppArg(ImakefileC);
}


int
main(int argc, char const *argv[])
{
	FILE	*	tmpfd = NULL;
	char	makeMacro[BUFSIZ];
	char	makefileMacro[BUFSIZ];
	int	lenCrossCompileDir = 0;

	program = (char *)argv[0];

	initSomeVariables();

	lenCrossCompileDir = strlen(CrossCompileDir);
	if (lenCrossCompileDir) {
		if (lenCrossCompileDir > (PATH_MAX - 20)) {
	      LogFatal("Cross compile directory path too long %s\n",
	      			CrossCompileDir);
		} else {
			CrossCompiling = TRUE;
	    }
	}

	setOpts(argc, (char **)argv);

	Imakefile = FindImakefile(Imakefile);

	CheckImakefileC(ImakefileC);

	if (Makefile) {
		tmpMakefile = Makefile;

		if ((tmpfd = fopen(tmpMakefile, "w+")) == NULL) {
			LogFatal("Cannot create temporary file %s.", tmpMakefile);
		}
	} else {
#ifdef HAS_MKSTEMP
		int fd;
#endif
		tmpMakefile = strdup(tmpMakefile);

#ifndef HAS_MKSTEMP
		if (mktemp(tmpMakefile) == NULL ||
		    (tmpfd = fopen(tmpMakefile, "w+")) == NULL) {
		   LogFatal("Cannot create temporary file %s.", tmpMakefile);
		}
#else
		fd = mkstemp(tmpMakefile);
		if (fd == -1 || (tmpfd = fdopen(fd, "w+")) == NULL) {
		   if (fd != -1) {
		      unlink(tmpMakefile);
		      close(fd);
		   }
		   LogFatal("Cannot create temporary file %s.", tmpMakefile);
		}
#endif
	}

	AddMakeArg("-f");
	AddMakeArg(tmpMakefile);
	sprintf(makeMacro, "MAKE=%s", program);
	AddMakeArg(makeMacro);
	sprintf(makefileMacro, "MAKEFILE=%s", Imakefile);
	AddMakeArg(makefileMacro);

	cleanedImakefile = CleanCppInput(Imakefile);

	exit(0);

	return 0;
}
